// Copyright (c) 2008 Michael Buckley

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#import <BTLSockets/BTLSockets.h>

#import "BTLSocketHandlerListTests.h"
#import "BTLLineEndingHandler.h"
#import "BTLXORHandler.h"
#import "BTLSelfRemovingHandler.h"
#import "BTLHLTestSocket.h"
#import "BTLTCPSocketTestDelegate.h"

@implementation BTLSocketHandlerListTests

- (void)testHandlerList
{
	BTLSocketHandlerList* testList = [BTLSocketHandlerList new];
	
	BTLHLTestSocket* sock = [BTLHLTestSocket new];
	[testList setSocket:sock];
	
	BTLLineEndingHandler* handler1 = [BTLLineEndingHandler new];
	[testList addHandlerToFront:handler1];
	
	BTLXORHandler* handler2 = [BTLXORHandler new];
	[testList addHandlerToFront:handler2];
	
	BTLSocketBuffer* data = [BTLSocketBuffer new];
	[data addData:"Test String" ofSize:12];
	
	BTLSocketIPAddress* addr = [BTLSocketIPAddress addressWithHostname:@"localhost" port:0 family:AF_INET error:nil];
	
	STAssertFalse([testList writeData:data toAddress:addr], nil);
	[testList connectionOpenedToAddress:addr];
	
	STAssertTrue([testList writeData:data toAddress:addr], nil);
	
	BTLSocketBuffer* result = [sock lastWritten];
	STAssertNotNil(result, nil);
	
	result = [handler2 doXOROnData:result];
	
	if(strcmp("Test String", [result getStringTerminatedByLinebreak]) != 0){
		STFail(@"First String not equal", nil);
	}
	
	[data release];
	data = [BTLSocketBuffer new];
	[data addData:"Test String\r\n" ofSize:14];
	BTLSocketBuffer* tempData = [handler2 doXOROnData:data];
	[data release];
	data = tempData;
	
	[testList removeAll];
	
	[testList addHandlerToEnd:handler2];
	[testList addHandlerToEnd:handler1];
	
	BTLTCPSocketTestDelegate* testDelegate = [BTLTCPSocketTestDelegate new];
	[sock setDelegate:testDelegate];
	
	[data release];
	data = [BTLSocketBuffer new];
	[data addData:"Test String\r\n" ofSize:14];
	tempData = [handler2 doXOROnData:data];
	[data release];
	data = tempData;
	
	[testList readData:data fromAddress:addr];
	
	result = [testDelegate lastMessage];
	STAssertNotNil(result, nil);
	
	if(strcmp("Test String", [result getStringTerminatedByLinebreak]) != 0){
		STFail(@"Second String not equal", nil);
	}
	
	BTLSocketHandlerList* copyList = [testList copy];
	
	[testList removeAll];
	
	[testList connectionClosedToAddress:addr];
	STAssertFalse([testDelegate currentlyConnected], nil);
	
	[copyList setSocket:sock];
	
	STAssertFalse([copyList writeData:data toAddress:addr], nil);
	[testList connectionOpenedToAddress:addr];
	
	[data release];
	data = [BTLSocketBuffer new];
	[data addData:"Test String\r\n" ofSize:14];
	tempData = [handler2 doXOROnData:data];
	[data release];
	data = tempData;
	
	[copyList readData:data fromAddress:addr];
	
	result = [testDelegate lastMessage];
	STAssertNotNil(result, nil);
	
	if(strcmp("Test String", [result getStringTerminatedByLinebreak]) != 0){
		STFail(@"Second String not equal", nil);
	}
	
	[copyList connectionClosedToAddress:addr];
	STAssertFalse([testDelegate currentlyConnected], nil);
	
	[copyList release];
	
	STAssertFalse([testDelegate currentlyConnected], nil);
	[testList connectionOpenedToAddress:addr];
	STAssertTrue([testDelegate currentlyConnected], nil);
	[testList connectionClosedToAddress:addr];
	STAssertFalse([testDelegate currentlyConnected], nil);
	
	STAssertFalse([testDelegate currentlyConnected], nil);
	[testList connectionOpenedToAddress:addr];
	STAssertTrue([testDelegate currentlyConnected], nil);
	
	BTLSelfRemovingHandler* srh1 = [BTLSelfRemovingHandler new];
	BTLSelfRemovingHandler* srh2 = [BTLSelfRemovingHandler new];
	BTLSelfRemovingHandler* srh3 = [BTLSelfRemovingHandler new];
	BTLSelfRemovingHandler* srh4 = [BTLSelfRemovingHandler new];
	
	[srh1 setMaxCount:2];
	[srh2 setMaxCount:4];
	[srh3 setMaxCount:1];
	[srh4 setMaxCount:3];
	
	[testList addHandlerToFront:srh4];
	[testList addHandlerToFront:srh3];
	[testList addHandlerToFront:srh2];
	[testList addHandlerToFront:srh1];
	
	[testList connectionClosedToAddress:addr];
	STAssertFalse([testDelegate currentlyConnected], nil);
	
	[data release];
	data = [BTLSocketBuffer new];
	
	[testList readData:data fromAddress:addr];
	result = [testDelegate lastMessage];
	STAssertEquals([result size], (int64_t) 4, nil);
	[testList readData:data fromAddress:addr];
	result = [testDelegate lastMessage];
	STAssertEquals([result size], (int64_t) 3, nil);
	[testList readData:data fromAddress:addr];
	result = [testDelegate lastMessage];
	STAssertEquals([result size], (int64_t) 2, nil);
	[testList readData:data fromAddress:addr];
	result = [testDelegate lastMessage];
	STAssertEquals([result size], (int64_t) 1, nil);
	[testList readData:data fromAddress:addr];
	result = [testDelegate lastMessage];
	STAssertEquals([result size], (int64_t) 0, nil);
	
	srh1 = [BTLSelfRemovingHandler new];
	srh2 = [BTLSelfRemovingHandler new];
	srh3 = [BTLSelfRemovingHandler new];
	srh4 = [BTLSelfRemovingHandler new];
	
	[srh1 setMaxCount:50];
	[srh2 setMaxCount:50];
	[srh3 setMaxCount:50];
	[srh4 setMaxCount:50];
	
	[testList addHandlerToFront:srh4];
	[testList addHandlerToFront:srh3];
	[testList addHandlerToFront:srh2];
	[testList addHandlerToFront:srh1];
	
	[testList connectionFailedToAddress:addr];
	STAssertTrue([testDelegate connectionFailed], nil);
	STAssertEquals([[sock lastWritten] size], (int64_t) 10, nil);
	
	[testList connectionClosedByRemoteAddress:addr];
	STAssertTrue([testDelegate remoteDisconnected], nil);
	STAssertEquals([[sock lastWritten] size], (int64_t) 10, nil);
	
	[testList connectionInterruptedToAddress:addr];
	STAssertTrue([testDelegate connectionInterrupted], nil);
	STAssertEquals([[sock lastWritten] size], (int64_t) 10, nil);
	
	[testList connectionClosedToAddress:addr];
	STAssertFalse([testDelegate currentlyConnected], nil);
	
	[testDelegate release];
	[result release]; //data == result
	[addr release];
	[handler2 release];
	[handler1 release];
	[testList release];
	[sock release];
}

@end
